package cn.stimd.spring.validation;

import cn.stimd.spring.beans.*;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;

import java.beans.PropertyEditor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * 1. DataBinder的两大功能是数据绑定和验证，bind和validate两个方法可以分别使用，也可以合在一起使用，视情况而定
 * 2. BindingResult则共享了绑定和验证两个阶段的目标对象以及错误列表
 * 3. 数据绑定是基于属性访问实现的，而数据验证是基于Java Validator接口及Hibernate的具体实现
 * <p>
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
    public static final String DEFAULT_OBJECT_NAME = "target";
    private final Object target;
    private final String objectName;
    private AbstractPropertyBindingResult bindingResult;
    private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
    private SimpleTypeConverter typeConverter = new SimpleTypeConverter();
    private ConversionService conversionService = new DefaultConversionService();
    //Spring验证器集合
    private final List<Validator> validators = new ArrayList<>();

    public DataBinder(Object target) {
        this(target, DEFAULT_OBJECT_NAME);
    }

    public DataBinder(Object target, String objectName) {
        this.target = target;
        this.objectName = objectName;
    }


    public void bind(PropertyValues pvs) {
        doBind((MutablePropertyValues) pvs);
    }


    /**
     * 数据绑定，实际的绑定操作是在BindingResult中完成的
     */
    protected void doBind(MutablePropertyValues mpvs) {
        try {
            getPropertyAccessor().setPropertyValues(mpvs);
        }catch (PropertyAccessException e){
            //处理属性访问时发生的错误
            this.bindingErrorProcessor.processPropertyAccessException(e, getInternalBindingResult());
        }
    }


    /**
     * 数据校验
     */
    public void validate() {
        for (Validator validator : this.validators) {
            validator.validate(getTarget(), getBindingResult());
        }
    }


    public Object getTarget() {
        return this.target;
    }

    public String getObjectName() {
        return objectName;
    }

    public void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) {
        this.bindingErrorProcessor = bindingErrorProcessor;
    }

    public ConversionService getConversionService() {
        return conversionService;
    }

    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
        this.typeConverter.setConversionService(conversionService);

        if (bindingResult != null) {
            this.bindingResult.initConversion(conversionService);
        }
    }


    //获取绑定对象的属性访问器
    protected PropertyAccessor getPropertyAccessor() {
        return getInternalBindingResult().getPropertyAccessor();
    }

    //获取BindingResult，默认使用BeanPropertyBindingResult
    public BindingResult getBindingResult() {
        return getInternalBindingResult();
    }

    //获取BindingResult，默认使用BeanPropertyBindingResult
    protected AbstractPropertyBindingResult getInternalBindingResult() {
        if (this.bindingResult == null) {
            BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(), getObjectName());
            result.initConversion(this.conversionService);
            this.bindingResult = result;
        }
        return this.bindingResult;
    }


    //尝试获取绑定对象的TypeConverter，如不存在则使用自带的
    protected TypeConverter getTypeConverter() {
        if (getTarget() != null) {
            return getInternalBindingResult().getPropertyAccessor();
        }
        else {
            return this.typeConverter;
        }
    }

    protected PropertyEditorRegistry getPropertyEditorRegistry() {
        if (getTarget() != null) {
            return getInternalBindingResult().getPropertyAccessor();
        }
        else {
            return this.typeConverter;
        }
    }

    public void setValidator(Validator validator) {
        this.validators.add(validator);
    }

    @Override
    public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
        getPropertyEditorRegistry().registerCustomEditor(requiredType, propertyEditor);
    }

    @Override
    public PropertyEditor findCustomEditor(Class<?> requiredType) {
        return getPropertyEditorRegistry().findCustomEditor(requiredType);
    }

    @Override
    public <T> T convertIfNecessary(Object value, Class<T> requiredType) {
        return getTypeConverter().convertIfNecessary(value,requiredType);
    }

    @Override
    public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) {
        return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
    }

    @Override
    public <T> T convertIfNecessary(Object value, Class<T> requiredType, Field field) {
        return getTypeConverter().convertIfNecessary(value, requiredType, field);
    }
}